<?php

// Namespace

namespace Loop;


// Dependencies

require_once(PATH_LOOP . 'Model/CProfilingThread.php');


/**
 * CProfilingManager is a class that allows to manage the profiling, i.e. start or stop the profiling process and return the results
 */

class CProfilingManager
{
	// Properties

	public $isPaused = false;
	public $profilingThread = null;


	/**
	 * Constructs a profiling manager
	 */

	public function __construct()
	{
		// Is profiling allowed?

		if ($this->isProfilingAllowed() === false)
		{
			return;
		}


		$this->profilingThread = new \Loop\CProfilingThread();

		$this->profilingThread->setCookieVariables(json_encode(v('COOKIE::*')));
		$this->profilingThread->setGetVariables(json_encode(v('GET::*')));
		$this->profilingThread->setHttpRequestVariables(json_encode(apache_request_headers()));
		$this->profilingThread->setHttpResponseVariables(json_encode(apache_response_headers()));
		$this->profilingThread->setMaxMemory(v('CApplication::maxMemoryAtStart'));
		$this->profilingThread->setPathToView(view()->getPath());
		$this->profilingThread->setPostVariables(json_encode(v('POST::*')));
		$this->profilingThread->setPreferences(json_encode(app()->getPreferenceManager()->getPreferences()));
		$this->profilingThread->setProfilingProcessKey(v('SESSION::profilingProcessKey'));
		$this->profilingThread->setSessionVariables(json_encode(v('SESSION::*')));
		$this->profilingThread->setStep(PROFILING_STEP_INIT);
		$this->profilingThread->setTimeOfStart(v('CApplication::timeOfStart'));
	}


	/**
	 * Starts a new profiling session
	 */

	public function api_start()
	{
		$this->start();
	}


	/**
	 * Stops the current profiling session
	 */

	public function api_stop()
	{
		$this->stop();
	}


	/**
	 * Gets and returns the current profiling threads
	 *
	 * @return 	array 	An array which contains the current profiling threads
	 */

	public function getRawProfilingThreads()
	{
		// Gets the current profiling threads

		$profilingThread = new \Loop\CProfilingThread();

		$rawProfilingThreads = $profilingThread->find
		(
			array
			(
				'conditions' => array
				(
					'profilingProcessKey' => array(EQUAL, v('SESSION::profilingProcessKey'))
				),
				'orderBy' => array('timeOfStart'),
				'orderDirection' => 'ascendent'
			)
		);


		return $rawProfilingThreads;
	}


	/**
	 * Gets and returns the profiling process results (duration, memory usage, the number of queries)
	 *
	 * @return 	array 	An array which contains the profiling results
	 */

	public function getSummaries()
	{

		$input = array();
		$result = array();


		// Summarize duration

		$value = round(microtime(true) - v('CApplication::timeOfStart'), 2);
		$maxValue = pref('[Loop]/profiling/duration/max');
		$percentage = round(($value / min(1, $maxValue)) * 100);

		$result['duration'] = array
		(
			'classOfIcon' => pref('[Loop]/profiling/duration/classOfIcon'),
			'label' => str('Profiling.Duration'),
			'maxValue' => $maxValue,
			'percentage' => $percentage,
			'value' => $value
		);


		// Summarize memory usage

		$value = round(memory_get_peak_usage() / 1024000, 2);
		$maxValue = round(pref('[Loop]/profiling/memory/max') / 1024000, 2);
		$percentage = round(($value / min(1, $maxValue)) * 100);

		$result['memory'] = array
		(
			'classOfIcon' => pref('[Loop]/profiling/memory/classOfIcon'),
			'label' => str('Profiling.Memory'),
			'maxValue' => $maxValue,
			'percentage' => $percentage,
			'value' => $value
		);


		// Summarize number of queries

		$value = v('::nbQueries');
		$maxValue = pref('[Loop]/profiling/query/max');
		$percentage = round(($value / max(1, $maxValue)) * 100);

		$result['query'] = array
		(
			'maxValue' => $maxValue,
			'value' => $value
		);


		//

		foreach ($result as $key => &$value)
		{
			//

			$value['classOfIcon'] = pref('[Loop]/profiling/' . $key . '/classOfIcon');
			$value['label'] = str('Profiling.' . ucfirst($key));
			$value['percentage'] = round(($value['value'] / max(1, $value['maxValue'])) * 100);

			
			//

			if ($value['percentage'] < 50)
			{
				$value['classOfFlag'] = pref('[Loop]/profiling/flag/low/classOfIcon');
			}
			else if ($value['percentage'] < 75)
			{
				$value['classOfFlag'] = pref('[Loop]/profiling/flag/medium/classOfIcon');
			}
			else if ($value['percentage'] <= 100)
			{
				$value['classOfFlag'] = pref('[Loop]/profiling/flag/high/classOfIcon');
			}
			else
			{
				$value['classOfFlag'] = pref('[Loop]/profiling/flag/critical/classOfIcon');
			}
		}


		return $result;
	}


	/**
	 * Checks if the current view can be profiled
	 *
	 * @return 	boolean 	true if the current view can be profiled else false
	 */

	public function isProfilingAllowed()
	{
		// Is profiling started?

		if ($this->isProfilingStarted() === false)
		{
			return false;
		}

		
		// Should this view be rejected?

		$rejectedViewNames = array
		(
			pref('[Loop]/views/pathToPageError.html'),
			'/[Loop]/Html/Page.Admin.html.html',
			'/[Loop]/Html/Page.Admin.html.html',
			'/[Loop]/Html/Page.Admin.Documentation.html',
			'/[Loop]/Html/Page.Admin.Profiling.html',
			'/[Loop]/Html/Page.Admin.Sandbox.html',
			'/[Loop]/Html/Page.Admin.Test.html',
			'/[Loop]/Html/Ui.ProfilingBar.html',
			'/[Loop]/Javascript/Page.Admin.Profiling.js',
			'/[Loop]/Javascript/Ui.ProfilingBar.js',
			'/[Loop]/Stylesheet/Page.Admin.Profiling.css',
			'/[Loop]/Stylesheet/Ui.Profiling.css'
		);

		if (in_array(view()->getPath(true), $rejectedViewNames) === true)
		{
			return false;
		}


		return true;
	}


	/**
	 * Checks if the profiling has started
	 *
	 * @return 	boolean 	true if the profiling has started else false	
	 */

	public function isProfilingStarted()
	{
		$profilingProcessKey = v('SESSION::profilingProcessKey');

		if
		(
			($this->isPaused == false) &&
			(empty($profilingProcessKey) === false)
		)
		{
			return true;
		}
		else
		{
			return false;
		}
	}


	/**
	 * Pause the current profiling session
	 */

	public function pause()
	{
		$this->isPaused = true;
	}


	/**
	 * Resume the current profiling session
	 */

	public function resume()
	{
		$this->isPaused = false;
	}


	/**
	 * Saves the profiling session and it thread
	 *
	 * @param 	array 	$objectProperties 	The profiling properties
	 * @param 	string 	$languageIsoCode 	The language to be save 	
	 */

	public function save($objectProperties = null, $languageIsoCode = null)
	{
		// Is profiling allowed?

		if ($this->isProfilingAllowed() === false)
		{
			return;
		}


		// Pause the profiling

		$this->pause();


		// Save the profiling thread

		$this->profilingThread->stop();
		$this->profilingThread->save();


		$this->pause();
	}


	/**
	 * Sets the profiling thread step
	 *
	 * @param 	string 	The profiling thread step name
	 */

	public function setStep($step)
	{
		// Is profiling allowed?

		if ($this->isProfilingAllowed() === false)
		{
			return;
		}


		// Save the profiling step

		$this->profilingThread->setStep($step);
	}


	/**
	 * Starts a new profiling session
	 */

	public function start()
	{
		// Remove all past processes, threads and events

		$profilingThread = new \Loop\CProfilingThread();
		$profilingThread->clean();

		$profilingEvent = new \Loop\CProfilingEvent();
		$profilingEvent->clean();


		// Share the process key in session

		v('SESSION::profilingProcessKey', app()->getStringHelper()->generateUniqueKey());
	}


	/**
	 * Starts an event in the profiling thread 
	 *
	 * @param 	string 	$eventType 			The type of the event
	 * @param 	string 	$eventMessage 		A message associated to the event	
	 * @param 	string 	$eventInput 		The elements that the event may need
	 *
	 * @return 	string 	The unique id of the event
	 */

	public function startEvent($eventType = null, $eventMessage = null, $eventInput = null)
	{
		// Is profiling allowed?

		if ($this->isProfilingAllowed() === false)
		{
			return;
		}


		// Start the event in the profiling thread

		return $this->profilingThread->startEvent($eventType, $eventMessage, $eventInput);
	}


	/**
	 * Stops the current profiling session
	 */

	public function stop()
	{
		// Is profiling allowed?

		if ($this->isProfilingAllowed() === false)
		{
			return;
		}


		//

		$this->profilingThread->stop();


		// Stop sharing the process key in session

		v('SESSION::profilingProcessKey', '');
	}


	/**
	 * Stops the given event in the profiling thread 
	 *
	 * @param 	string 	$eventUuid 			The unique Id of the event
	 * @param 	string 	$eventOutput 		The output of the event	
	 */

	public function stopEvent($eventUuid, $eventOutput)
	{
		// Is profiling allowed?

		if ($this->isProfilingAllowed() === false)
		{
			return;
		}


		// Stop the event in the profiling thread

		$this->profilingThread->stopEvent($eventUuid, $eventOutput);
	}
}

?>
